<script setup lang="ts">
import { ref, computed, watch } from 'vue'

interface UploadFile {
  id: string
  name: string
  size: number
  type: string
  url?: string
  status: 'pending' | 'uploading' | 'success' | 'error'
  progress: number
  error?: string
}

interface Props {
  modelValue?: UploadFile[]
  accept?: string
  multiple?: boolean
  maxSize?: number // MB
  maxFiles?: number
  disabled?: boolean
  showPreview?: boolean
  uploadUrl?: string
  headers?: Record<string, string>
  data?: Record<string, any>
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: () => [],
  accept: '*/*',
  multiple: true,
  maxSize: 10,
  maxFiles: 5,
  disabled: false,
  showPreview: true
})

const emit = defineEmits<{
  'update:modelValue': [files: UploadFile[]]
  'upload-success': [file: UploadFile, response: any]
  'upload-error': [file: UploadFile, error: any]
  'upload-progress': [file: UploadFile, progress: number]
  'remove': [file: UploadFile]
}>()

const fileInput = ref<HTMLInputElement>()
const dragOver = ref(false)
const uploading = ref(false)

const files = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})

// 生成唯一ID
const generateId = () => Date.now().toString(36) + Math.random().toString(36).substr(2)

// 格式化文件大小
const formatFileSize = (bytes: number) => {
  if (bytes === 0) return '0 Bytes'
  const k = 1024
  const sizes = ['Bytes', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

// 获取文件图标
const getFileIcon = (type: string) => {
  if (type.startsWith('image/')) return 'mdi-file-image'
  if (type.includes('pdf')) return 'mdi-file-pdf-box'
  if (type.includes('word')) return 'mdi-file-word'
  if (type.includes('excel') || type.includes('sheet')) return 'mdi-file-excel'
  if (type.includes('zip') || type.includes('rar')) return 'mdi-zip-box'
  return 'mdi-file'
}

// 获取状态颜色
const getStatusColor = (status: string) => {
  switch (status) {
    case 'success': return 'success'
    case 'error': return 'error'
    case 'uploading': return 'primary'
    default: return 'grey'
  }
}

// 验证文件
const validateFile = (file: File): string | null => {
  // 检查文件大小
  if (file.size > props.maxSize * 1024 * 1024) {
    return `文件大小不能超过 ${props.maxSize}MB`
  }
  
  // 检查文件数量
  if (!props.multiple && files.value.length >= 1) {
    return '只能上传一个文件'
  }
  
  if (files.value.length >= props.maxFiles) {
    return `最多只能上传 ${props.maxFiles} 个文件`
  }
  
  return null
}

// 处理文件选择
const handleFileSelect = (fileList: FileList | File[]) => {
  const fileArray = Array.from(fileList)
  
  fileArray.forEach(file => {
    const error = validateFile(file)
    
    const uploadFile: UploadFile = {
      id: generateId(),
      name: file.name,
      size: file.size,
      type: file.type,
      status: error ? 'error' : 'pending',
      progress: 0,
      error
    }
    
    if (file.type.startsWith('image/') && props.showPreview) {
      const reader = new FileReader()
      reader.onload = (e) => {
        uploadFile.url = e.target?.result as string
      }
      reader.readAsDataURL(file)
    }
    
    files.value = [...files.value, uploadFile]
    
    // 如果文件有效且有上传URL，开始上传
    if (!error && props.uploadUrl) {
      uploadFile(uploadFile, file)
    }
  })
}

// 上传文件
const uploadFile = async (uploadFile: UploadFile, file: File) => {
  uploadFile.status = 'uploading'
  uploading.value = true
  
  const formData = new FormData()
  formData.append('file', file)
  
  // 添加额外数据
  if (props.data) {
    Object.entries(props.data).forEach(([key, value]) => {
      formData.append(key, value)
    })
  }
  
  try {
    const xhr = new XMLHttpRequest()
    
    // 监听上传进度
    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable) {
        uploadFile.progress = Math.round((e.loaded / e.total) * 100)
        emit('upload-progress', uploadFile, uploadFile.progress)
      }
    }
    
    xhr.onload = () => {
      if (xhr.status === 200) {
        const response = JSON.parse(xhr.responseText)
        uploadFile.status = 'success'
        uploadFile.progress = 100
        uploadFile.url = response.url || uploadFile.url
        emit('upload-success', uploadFile, response)
      } else {
        uploadFile.status = 'error'
        uploadFile.error = `上传失败: ${xhr.statusText}`
        emit('upload-error', uploadFile, xhr.statusText)
      }
      uploading.value = false
    }
    
    xhr.onerror = () => {
      uploadFile.status = 'error'
      uploadFile.error = '网络错误'
      emit('upload-error', uploadFile, '网络错误')
      uploading.value = false
    }
    
    xhr.open('POST', props.uploadUrl)
    
    // 设置请求头
    if (props.headers) {
      Object.entries(props.headers).forEach(([key, value]) => {
        xhr.setRequestHeader(key, value)
      })
    }
    
    xhr.send(formData)
  } catch (error) {
    uploadFile.status = 'error'
    uploadFile.error = '上传异常'
    emit('upload-error', uploadFile, error)
    uploading.value = false
  }
}

// 移除文件
const removeFile = (file: UploadFile) => {
  const index = files.value.findIndex(f => f.id === file.id)
  if (index > -1) {
    files.value.splice(index, 1)
    emit('remove', file)
  }
}

// 点击上传
const triggerUpload = () => {
  if (!props.disabled) {
    fileInput.value?.click()
  }
}

// 拖拽处理
const handleDragOver = (e: DragEvent) => {
  e.preventDefault()
  if (!props.disabled) {
    dragOver.value = true
  }
}

const handleDragLeave = (e: DragEvent) => {
  e.preventDefault()
  dragOver.value = false
}

const handleDrop = (e: DragEvent) => {
  e.preventDefault()
  dragOver.value = false
  
  if (props.disabled) return
  
  const files = e.dataTransfer?.files
  if (files) {
    handleFileSelect(files)
  }
}

// 重新上传
const retryUpload = (uploadFile: UploadFile) => {
  const fileInput = document.createElement('input')
  fileInput.type = 'file'
  fileInput.accept = props.accept
  fileInput.onchange = (e) => {
    const file = (e.target as HTMLInputElement).files?.[0]
    if (file) {
      uploadFile.status = 'pending'
      uploadFile.progress = 0
      uploadFile.error = undefined
      uploadFile(uploadFile, file)
    }
  }
  fileInput.click()
}
</script>

<template>
  <div class="file-upload-container">
    <!-- 上传区域 -->
    <div 
      class="upload-area"
      :class="{ 
        'drag-over': dragOver, 
        'disabled': disabled,
        'has-files': files.length > 0
      }"
      @dragover="handleDragOver"
      @dragleave="handleDragLeave"
      @drop="handleDrop"
      @click="triggerUpload"
    >
      <input
        ref="fileInput"
        type="file"
        :accept="accept"
        :multiple="multiple"
        :disabled="disabled"
        style="display: none"
        @change="(e) => handleFileSelect((e.target as HTMLInputElement).files!)"
      >
      
      <div class="upload-content">
        <VIcon 
          :icon="dragOver ? 'mdi-cloud-upload' : 'mdi-cloud-upload-outline'"
          size="48"
          :color="dragOver ? 'primary' : 'grey'"
          class="upload-icon"
        />
        <h3 class="upload-title">
          {{ dragOver ? '释放文件开始上传' : '点击或拖拽文件到这里上传' }}
        </h3>
        <p class="upload-desc">
          支持格式: {{ accept === '*/*' ? '所有格式' : accept }}，
          单个文件最大 {{ maxSize }}MB，
          最多上传 {{ maxFiles }} 个文件
        </p>
      </div>
    </div>

    <!-- 文件列表 -->
    <div v-if="files.length > 0" class="file-list">
      <VCard
        v-for="file in files"
        :key="file.id"
        class="file-item"
        :class="{ 'upload-error': file.status === 'error' }"
        elevation="2"
      >
        <VCardText class="file-content">
          <!-- 文件预览/图标 -->
          <div class="file-preview">
            <VImg
              v-if="file.url && file.type.startsWith('image/')"
              :src="file.url"
              class="preview-image"
              cover
            />
            <VIcon
              v-else
              :icon="getFileIcon(file.type)"
              size="32"
              :color="getStatusColor(file.status)"
            />
          </div>
          
          <!-- 文件信息 -->
          <div class="file-info">
            <h4 class="file-name">{{ file.name }}</h4>
            <p class="file-size">{{ formatFileSize(file.size) }}</p>
            <p v-if="file.error" class="file-error">{{ file.error }}</p>
            
            <!-- 进度条 -->
            <VProgressLinear
              v-if="file.status === 'uploading'"
              :model-value="file.progress"
              color="primary"
              height="4"
              class="file-progress"
            />
          </div>
          
          <!-- 操作按钮 -->
          <div class="file-actions">
            <!-- 状态图标 -->
            <VIcon
              v-if="file.status === 'success'"
              icon="mdi-check-circle"
              color="success"
              size="20"
            />
            <VIcon
              v-else-if="file.status === 'uploading'"
              icon="mdi-loading"
              color="primary"
              size="20"
              class="rotating"
            />
            <VIcon
              v-else-if="file.status === 'error'"
              icon="mdi-alert-circle"
              color="error"
              size="20"
            />
            
            <!-- 重试按钮 -->
            <VBtn
              v-if="file.status === 'error' && uploadUrl"
              icon="mdi-refresh"
              size="small"
              variant="text"
              color="primary"
              @click.stop="retryUpload(file)"
            />
            
            <!-- 删除按钮 -->
            <VBtn
              icon="mdi-close"
              size="small"
              variant="text"
              color="error"
              @click.stop="removeFile(file)"
            />
          </div>
        </VCardText>
      </VCard>
    </div>
  </div>
</template>

<style scoped>
.file-upload-container {
  width: 100%;
}

.upload-area {
  border: 2px dashed #e0e0e0;
  border-radius: 16px;
  padding: 40px 20px;
  text-align: center;
  cursor: pointer;
  transition: all 0.3s ease;
  background: linear-gradient(135deg, #fafafa 0%, #ffffff 100%);
  min-height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.upload-area:hover:not(.disabled) {
  border-color: #1976d2;
  background: linear-gradient(135deg, #e3f2fd 0%, #ffffff 100%);
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(25, 118, 210, 0.15);
}

.upload-area.drag-over {
  border-color: #1976d2;
  background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 50%, #ffffff 100%);
  transform: scale(1.02);
}

.upload-area.disabled {
  cursor: not-allowed;
  opacity: 0.6;
  background: #f5f5f5;
}

.upload-area.has-files {
  padding: 20px;
  min-height: 120px;
}

.upload-content {
  max-width: 400px;
  margin: 0 auto;
}

.upload-icon {
  margin-bottom: 16px;
  transition: all 0.3s ease;
}

.upload-area:hover .upload-icon {
  transform: translateY(-4px);
}

.upload-title {
  font-size: 1.25rem;
  font-weight: 600;
  color: #1e293b;
  margin-bottom: 8px;
}

.upload-desc {
  color: #64748b;
  font-size: 0.875rem;
  margin: 0;
  line-height: 1.5;
}

.file-list {
  margin-top: 20px;
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

.file-item {
  border-radius: 12px;
  overflow: hidden;
  transition: all 0.3s ease;
}

.file-item:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}

.file-item.upload-error {
  border: 1px solid #ef4444;
}

.file-content {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px;
}

.file-preview {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 8px;
  overflow: hidden;
  background: #f5f5f5;
  display: flex;
  align-items: center;
  justify-content: center;
}

.preview-image {
  width: 100%;
  height: 100%;
}

.file-info {
  flex: 1;
  min-width: 0;
}

.file-name {
  font-size: 0.875rem;
  font-weight: 600;
  color: #1e293b;
  margin: 0 0 4px 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.file-size {
  font-size: 0.75rem;
  color: #64748b;
  margin: 0;
}

.file-error {
  font-size: 0.75rem;
  color: #ef4444;
  margin: 4px 0 0 0;
}

.file-progress {
  margin-top: 8px;
}

.file-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

.rotating {
  animation: rotate 1s linear infinite;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@media (max-width: 768px) {
  .upload-area {
    padding: 20px 16px;
    min-height: 150px;
  }
  
  .upload-title {
    font-size: 1.125rem;
  }
  
  .upload-desc {
    font-size: 0.8rem;
  }
  
  .file-list {
    grid-template-columns: 1fr;
  }
}
</style>
